#pragma once

#ifdef new
#  undef new
#endif
#ifdef delete
#  undef delete
#endif

namespace cpac
{
    // Declare a base class for easy specialisation of const and non-const
	//--------------------------------------
	template< class _Type >
	struct allocator_stl_base
	{
		typedef _Type value_type;
	};

	template< class _Type >
	struct allocator_stl_base< const _Type >
	{
		typedef _Type value_type;
	};
	//--------------------------------------

	// Replacement allocator for STL containers
	//--------------------------------------
	template< class _Type, class _Alloc_Policy >
	class allocator_stl
		: public allocator_stl_base< _Type >
	{
	public:
		typedef allocator_stl_base< _Type >	base;
		typedef typename base::value_type   value_type;
		typedef value_type*		            pointer;
		typedef value_type&		            reference;
		typedef value_type const*	        const_pointer;
		typedef value_type const&	        const_reference;
		typedef std::size_t					size_type;
		typedef std::ptrdiff_t				difference_type;

		template<typename U>
		struct rebind
		{
			typedef allocator_stl<U, _Alloc_Policy> other;
		};

		inline explicit allocator_stl()
		{ }

		virtual ~allocator_stl()
		{ }

		inline allocator_stl( allocator_stl const& )
		{ }

		template <typename U>
		inline allocator_stl( allocator_stl<U, _Alloc_Policy> const& )
		{ }

		template <typename U, typename P>
		inline allocator_stl( allocator_stl<U, P> const& )
		{ }

		/// memory allocation (elements, used by STL)
		inline pointer allocate( size_type in_count,
			typename std::allocator<void>::const_pointer in_ptr = 0 )
		{
                        (void)in_ptr;
			// convert request to bytes
			register size_type sz = in_count*sizeof( _Type );
			pointer p  = static_cast<pointer>(_Alloc_Policy::allocate_bytes(sz));
			return p;
		}

		/// memory deallocation (elements, used by STL)
		inline void deallocate( pointer in_ptr, size_type )
		{
			// convert request to bytes, but we can't use this?
			// register size_type sz = count*sizeof( T );
			_Alloc_Policy::deallocate_bytes(in_ptr);
		}

		pointer address(reference in_x) const
		{
			return &in_x;
		}

		const_pointer address(const_reference in_x) const
		{
			return &in_x;
		}

		size_type max_size() const throw()
		{
			// maximum size this can handle, delegate
			return _Alloc_Policy::get_max_allocation_size();
		}

		void construct(pointer in_ptr, _Type const& in_val)
		{
			// call placement new
			new(static_cast<void*>(in_ptr)) _Type(in_val);
		}

		void destroy(pointer in_ptr)
		{
			// do we have to protect against non-classes here?
			// some articles suggest yes, some no
			in_ptr->~_Type();
		}
	};
	//--------------------------------------

	// Comparison operators
	//--------------------------------------
	template< typename _Type, typename _Type2, typename _Policy >
	inline bool operator==(allocator_stl< _Type, _Policy > const&, allocator_stl< _Type2, _Policy > const&)
	{
		// same alloc policy (P), memory can be freed
		return true;
	}

	template< typename _Type, typename _Policy, typename _Other_Allocator >
	inline bool operator==(allocator_stl< _Type, _Policy > const&, _Other_Allocator const&)
	{
		return false;
	}

	template< typename _Type, typename _Type2, typename _Policy >
	inline bool operator!=(allocator_stl< _Type, _Policy > const&, allocator_stl< _Type2, _Policy > const&)
	{
		// same alloc policy (P), memory can be freed
		return false;
	}

	template< typename _Type, typename _Policy, typename _Other_Allocator >
	inline bool operator!=(allocator_stl< _Type, _Policy > const&, _Other_Allocator const&)
	{
		return true;
	}
	//--------------------------------------
};
